home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / metkit / catalog.cpp < prev    next >
C/C++ Source or Header  |  1997-06-07  |  8KB  |  224 lines

  1. //    Copyright (C) 1996, 1997 Meta Four Software.  All rights reserved.
  2. //
  3. //  Recursive directory scanner sample code
  4. //
  5. //! rev="$Id: catalog.cpp,v 1.3 1997/05/27 00:06:33 jcw Rel $"
  6.  
  7. /////////////////////////////////////////////////////////////////////////////
  8. //
  9. //  The following two globally accesible routines are defined below:
  10. //                  
  11. //      c4_View fScanDirectories(const char* path_);
  12. //      c4_String fFullPath(c4_View& dirs_, int dirNum_);
  13. //      
  14. //  The fScanDirectories routine does all the work, and completely hides all
  15. //  Windows 16/32 specifics by returning a generalized catalog tree object.
  16. //  In contrast with normal C++ conventions, nearly all MetaKit objects are
  17. //  reference counted. This takes care of fully automatic cleanup after use.
  18. //  
  19. //  The structure of the object returned by fScanDirectories is as follows:
  20. //  
  21. //      Property        Type        Description
  22. //      ========        ====        ===========
  23. //          
  24. //      dirs            nested      Contains 1 record per directory
  25. //          parent      integer     Index of parent entry
  26. //          name        string      Name of this directory
  27. //          files       nested      Contains 1 record per file entry
  28. //              name    string      Name of this file
  29. //              size    string      File size
  30. //              date    integer     Modification date (DOS format 7+4+5 bits)
  31. //  
  32. //  The first directory entry (entry 0) is special: the parent is set to zero
  33. //  (points to itself), and the name is the base path name (see path_ arg).
  34. //              
  35. //  In C++ notation, a declaration for this object would be something like:
  36. //  
  37. //      struct
  38. //      {
  39. //          int parent;
  40. //          char* name;
  41. //  
  42. //          struct
  43. //          {
  44. //              char* name;
  45. //              long size;
  46. //              int date;
  47. //  
  48. //          } files [];                 // variable size, not allowed in C++
  49. //  
  50. //      } dirs [];
  51. //
  52. /////////////////////////////////////////////////////////////////////////////
  53.  
  54. #include "stdafx.h"
  55. #include "catalog.h"
  56.  
  57.     // forward declaration
  58. static void ScanSubDir(c4_View&, int, const c4_String&);
  59.  
  60. /////////////////////////////////////////////////////////////////////////////
  61. // Property definitions
  62.  
  63.     c4_ViewProp     pFiles ("files");
  64.     c4_IntProp      pParent ("parent"),
  65.                     pSize ("size"),
  66.                     pDate ("date");
  67.     c4_StringProp   pName ("name");
  68.  
  69. /////////////////////////////////////////////////////////////////////////////
  70. // Scan a directory tree and return a corresponding structure for it
  71.  
  72. c4_View fScanDirectories(const char* path_)
  73. {
  74.         // Start with a view containing the root directory entry
  75.     c4_View dirs;
  76.     dirs.Add(pName [path_]);
  77.     
  78.         // This loop "automagically" handles the recursive traversal of all
  79.         // subdirectories. The trick is that each scan may add new entries
  80.         // at the end, causing this loop to continue (GetSize() changes!).
  81.         
  82.     for (int i = 0; i < dirs.GetSize(); ++i)
  83.     {
  84.         c4_String path = fFullPath(dirs, i);
  85.  
  86.         TRACE("Scanning '%s' ...\n", (const char*) path);
  87.         ScanSubDir(dirs, i, path);
  88.     }
  89.     
  90.         // The returned object contains the entire directory tree.
  91.         // Everything is automatically destroyed when no longer referenced. 
  92.     return dirs;
  93. }
  94.  
  95. /////////////////////////////////////////////////////////////////////////////
  96. // Reconstruct the full path name from a subdirectory index in the tree
  97.  
  98. c4_String fFullPath(c4_View& dirs_, int dirNum_)
  99. {
  100.         // Prefix all parent dir names until the root level is reached
  101.     c4_String path;
  102.     for (;;)
  103.     {
  104.         path = pName (dirs_[dirNum_]) + "\\" + path;
  105.  
  106.         if (dirNum_ == 0)
  107.             return path; // this result always has a trailing backslash
  108.             
  109.         dirNum_ = (int) pParent (dirs_[dirNum_]);
  110.     }
  111. }
  112.  
  113. /////////////////////////////////////////////////////////////////////////////
  114. // There are two versions of ScanSubDir, one for Win32 and one for Win16
  115.  
  116. #ifdef _WIN32
  117.  
  118.         // Convert a Win32 filedate back to the good old DOS format
  119.     static WORD DosDate(FILETIME& ft)
  120.     {
  121.         SYSTEMTIME st;
  122.         
  123.         if (!FileTimeToSystemTime(&ft, &st))
  124.             return 0;
  125.             
  126.         return (WORD) (((st.wYear-1980) << 9) | (st.wMonth << 5) | st.wDay);
  127.     }
  128.     
  129.         // Scan subdirectory and update the directory structure
  130.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const c4_String& path_)
  131.     {
  132.         WIN32_FIND_DATA fd;
  133.         
  134.         HANDLE h = FindFirstFile(path_ + "*", &fd);
  135.         if (h)
  136.         {
  137.                 //  Some notes on efficiency:
  138.                 //
  139.                 //  1)  It is probably faster to fill a view with data, and
  140.                 //      then store it in a persistent field, than to modify
  141.                 //      persistent structures in place. In this example, this
  142.                 //      is needed anyway, since we also sort all filenames.
  143.                 //
  144.                 //  2)  If possible, avoid constructing rows inside a loop.
  145.                 //      For that reason, both 'prop [const]' and 'row + row'
  146.                 //      are relatively inefficient.
  147.  
  148.         //  c4_View files = pFiles (dirs_[dirNum_]);
  149.             c4_View files;
  150.             c4_Row dir, file;
  151.  
  152.             do
  153.             {
  154.                 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  155.                 {
  156.                     if (fd.cFileName[0] == '.')
  157.                         continue;
  158.  
  159.                 //  dirs_.Add(pParent [dirNum_] + pName [fd.cFileName]);
  160.                     pParent (dir) = dirNum_;
  161.                     pName (dir) = fd.cFileName;
  162.                     dirs_.Add(dir);
  163.                 }            
  164.                 else
  165.                 {
  166.                 //  files.Add(pName [fd.cFileName] + pSize [fd.nFileSizeLow]
  167.                 //              + pDate [DosDate(fd.ftLastWriteTime)]);
  168.                     pName (file) = fd.cFileName;
  169.                     pSize (file) = fd.nFileSizeLow;
  170.                     pDate (file) = DosDate(fd.ftLastWriteTime);
  171.                     files.Add(file);
  172.                 }
  173.                 
  174.             } while (FindNextFile(h, &fd));
  175.             
  176.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  177.             
  178.             FindClose(h);
  179.         }
  180.     }
  181.  
  182. #else
  183.  
  184.     #include <dos.h>
  185.     
  186.         // Scan subdirectory and update the directory structure
  187.     static void ScanSubDir(c4_View& dirs_, int dirNum_, const c4_String& path_)
  188.     {
  189.         _find_t fd;
  190.         
  191.         if (_dos_findfirst(path_ + "*.*", _A_SUBDIR, &fd) == 0)
  192.         {
  193.             c4_View files;
  194.             c4_Row dir, file;
  195.  
  196.             do
  197.             {
  198.                 if (fd.attrib & _A_SUBDIR)
  199.                 {
  200.                     if (fd.name[0] == '.')
  201.                         continue;
  202.  
  203.                     pParent (dir) = dirNum_;
  204.                     pName (dir) = fd.name;
  205.                     dirs_.Add(dir);
  206.                 }            
  207.                 else
  208.                 {
  209.                     pName (file) = fd.name;
  210.                     pSize (file) = fd.size;
  211.                     pDate (file) = fd.wr_date;
  212.                     files.Add(file);
  213.                 }
  214.                 
  215.             } while (_dos_findnext(&fd) == 0);
  216.             
  217.             pFiles (dirs_[dirNum_]) = files.SortOn(pName);
  218.         }
  219.     }
  220.  
  221. #endif
  222.     
  223. /////////////////////////////////////////////////////////////////////////////
  224.